<?php

namespace Library;

use ReflectionClass;
use ReflectionMethod;
use RuntimeException;
use ReflectionException;
use BadMethodCallException;

class Ioc
{
    /**
     * @param App $app
     */
    public function __construct(protected App $app) {
        $actionName = $app->getActionName();
        $actionName = str_replace([" ", "-"],"_",$actionName);
        $class = $app->getControllerClass();
        if (!class_exists($class)){
            throw new RuntimeException(sprintf("Class Error: 【%s】 is not exit！", $class));
        }
        if (!method_exists($class,$actionName)){
            throw new BadMethodCallException(sprintf("%s::%s() is not exit！", $class,$actionName));
        }
        $this->render($class,$actionName);
    }

    /**
     * @param object|string $objectClass
     * @param string $actionName
     * @return void
     */
    public function render(object|string $objectClass, string $actionName): void
    {
        try {
            // 创建容器
            $reflector = new ReflectionClass($objectClass);
            list($constructArgs,$methodArgs) = $this->getReflectionMethodParams($objectClass);
            $newInstance = $reflector->newInstanceArgs($constructArgs);
        } catch (ReflectionException $e) {
            throw new RuntimeException(sprintf("Class Error: %s", $e->getMessage()));
        }
        $response = $newInstance->{$actionName}(...$methodArgs);
        if ($response instanceof Response){
            echo $response->toString();
            exit;
        }
    }

    /**
     * 获取类的所有方法的参数数组集
     * @param string $className
     * @param bool $findMethod
     * @return array
     */
    public function getReflectionMethodParams(string $className, bool  $findMethod = true): array
    {
        $constructArgs = [];
        $methodArgs = [];
        try {
            $reflector = new ReflectionClass($className);
            $construct = $reflector->getConstructor();
            if($construct){
                $constructArgs = $this->handleMethodParameters($construct);
            }
            if ($findMethod){
                $actionName = $this->app->getActionName();
                $actionName = str_replace([" ","-"],"_",$actionName);
                $method = $reflector->getMethod($actionName);
                $methodArgs = $this->handleMethodParameters($method, $className);
            }
        } catch (ReflectionException $e) {
            throw new RuntimeException($e->getMessage());
        }
        return [$constructArgs,$methodArgs];
    }

    /**
     * @param ReflectionMethod $method
     * @param string|null $className
     * @return array
     */
    protected function handleMethodParameters(ReflectionMethod $method,  string $className = null):array
    {
        $paramArr = [];
        try {
            $needParams = $method->getParameters();
            foreach($needParams as $needParam){
                $objectName = $needParam->getType()->getName();
                if(class_exists($objectName)){
                    list($constructArgs) = $this->getReflectionMethodParams($objectName, false);
                    $paramArr[] = (new ReflectionClass($objectName))->newInstanceArgs($constructArgs);
                }elseif($className){
                    list($constructArgs) = $this->getReflectionMethodParams($className, false);
                    $paramArr[] = (new ReflectionClass($className))->newInstanceArgs($constructArgs);
                }
            }
        } catch (ReflectionException $e) {
            throw new RuntimeException($e->getMessage());
        }
        return $paramArr;
    }


    /**
     * @param App $app
     * @return Ioc
     */
    public static function run(App $app):Ioc
    {
        return new self($app);
    }

}